home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / PROGRAMM / PASCAL / 0920.ZIP / TPAS4.ARC / TPAS4.TXT
Text File  |  1979-12-31  |  32KB  |  862 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.                               Turbo Pascal Version 4.0
  17.  
  18.                                     Observations
  19.  
  20.                                       12/06/87
  21.  
  22.  
  23.  
  24.                     Introduction and Comments:                 2
  25.  
  26.                     3.x compared with 4.0:                     3
  27.  
  28.                     Value assignment speed tests:              5
  29.  
  30.                     Incrementing / decrementing integers:      6
  31.  
  32.                     Control variable (counters) speed tests:   6
  33.  
  34.                     Loop structure speed tests:                7
  35.  
  36.                     Procedure and Function speed tests:        7
  37.           
  38.                     String manipulation speed tests:           8
  39.  
  40.                     Screen I/O speed tests:                    9
  41.  
  42.                     Simple / useful routines:                 10
  43.  
  44.  
  45.  
  46.  
  47.  
  48.          Introduction and Comments:
  49.  
  50.              When I received the upgrade to 4.0 for Turbo Pascal I was 
  51.          mildly excited.  But as I started going through the new manual, 
  52.          experimenting and trying out some of the new features, I felt like 
  53.          a kid with a new toy.  Turbo Pascal 4.0 is nothing short of 
  54.          awesome!  I love it!
  55.  
  56.              Borland has certainly outdone themselves with this release.  
  57.          It's faster, it's more powerful, it's easier to use, and a big 
  58.          surprise is that it can generate a smaller .EXE file than an 
  59.          equivalent .COM file in version 3.x.
  60.  
  61.              In the README documentation on the compiler distribution 
  62.          diskette it is mentioned that the system unit traps interrupt 24 
  63.          (DOS Critical Error) automatically and allows error checking via 
  64.          the usual IOResult with {$I-}.  That sounded remarkable!  It 
  65.          would sure simplify a number of things, but for some reason the 
  66.          old "KeyPressed" function came to mind.  So the very first thing  
  67.          tried was to get the free space on a floppy diskette in an empty 
  68.          drive (i.e. no diskette present, door open) ... it worked like a 
  69.          champ.  No ugly DOS "Abort, Retry, Ignore" message, no runtime 
  70.          error, just an error code in IOResult.  Same thing with an 
  71.          unformatted diskette.  Very impressive!  By the way, the new 
  72.          "KeyPressed" function works great too!
  73.  
  74.              Borland has done the programming community a tremendous favor 
  75.          with this release.  It is superb.
  76.  
  77.              There are two minor things I wouldn't mind seeing changed in 
  78.          the next update.  The Ctrl-F1 syntax help feature is tremendous, 
  79.          it will really save wear and tear on the manuals.  However, it  
  80.          would be nice to see the ability to add new information to these help 
  81.          windows, to cover your own custom routines.  Well, maybe you can 
  82.          and I just haven't figured out how yet.  The other thing is in the 
  83.          editor's Find-And-Replace defaults.  I rarely use the same search 
  84.          strings twice in a row, but saving the last search string is kind 
  85.          of a neat idea.  However, positioning the cursor at the end of the 
  86.          string is rather confusing at first.  Sure, if you press any 
  87.          character key the field is cleared and ready for a brand new 
  88.          search string, but it would be less confusing if the cursor were 
  89.          placed at the beginning of the string.  Pressing <enter> would 
  90.          still retain the default, and it would be a little more intuitive.  
  91.          But these are just dippy little complaints and perhaps not even 
  92.          worth mentioning here.
  93.  
  94.              At this point there is no doubt that release 4.0 will simplify 
  95.          and shorten the programming process, in addition to generating 
  96.          faster and more compact code.  It's what a programming language
  97.          should be!
  98.  
  99.              In order to compare various operations for speed, a looping   
  100.          structure was set up which allowed the item tested to be
  101.          executed between .5 and 34 million times, depending on the 
  102.          operation, with 5 repetitions of the loop to average the results.
  103.  
  104.  
  105.                                       Page 2
  106.  
  107.  
  108.  
  109.  
  110.  
  111.              For timing purposes, it's easy to use the PC's master clock 
  112.          count stored in the ROM communication area of low memory (in the 
  113.          0 block) at offset $046C.  This 4 byte (2 word) number is used to
  114.          record each clock tick (occurring about 18.2 times per second) from 
  115.          midnight to midnight.  A LongInt really isn't necessary because we
  116.          don't need the whole number for these relatively short and simple
  117.          tests.  For more information refer to chapter 3 of Peter Norton's 
  118.          "Programmers Guide to the IBM PC."  This is basically the same 
  119.          technique used in Brian Foley's QKSTR.PAS demonstration.  For 
  120.          these simple timing tests an absolute variable was declared as
  121.          follows:
  122.  
  123.              Var
  124.                MasterClock : Word ABSOLUTE $0000:$046C;
  125.  
  126.          While $0040:$006C is also correct, the other style helps you
  127.          remember that it is in the zero block of memory.
  128.  
  129.              These tests were run on a Kaypro PC-10 (NEC V20 processor) 
  130.          running at 8 MHz.  Execution times may vary depending on your 
  131.          machine.
  132.  
  133.  
  134.          Turbo Pascal 3.1 vs. 4.0:
  135.  
  136.              First the empty loop structure was timed, and then compared
  137.          with the extra time it took to run with the test code inserted
  138.          into the loop.  Here are some interesting statistics which
  139.          turned up.
  140.  
  141.              Version 3.1 .COM file size     11,575 bytes
  142.              Version 4.0 .EXE file size      5,264 bytes
  143.  
  144.          So version 4.0 was about 65% smaller.
  145.  
  146.          The 3.1 .COM file was optimized using Turbo Optimizer version 
  147.          1.02 by Turbo Power Software (TOPT defaults, TLC compact mode).   
  148.          The optimized .COM file size was 2,052 bytes, or about 82% smaller 
  149.          than the 3.1 version, and about 61% smaller that the 4.0 version.
  150.  
  151.          This was not too surprising.  As for speed differences, the 
  152.          results were as follows:
  153.  
  154.              Version 4.0 was about 8% faster than version 3.1 in the empty 
  155.              loop timing tests.
  156.  
  157.          The optimized version of 3.1 was about 11% faster than the version 
  158.          4.0 code.  Release 4.0 of Turbo Pascal will not make Turbo
  159.          Optimizer obsolete.  Perhaps a new version of the Optimizer
  160.          will soon be released.  The improvements made by Borland 
  161.          may eliminate the need for TLC (Turbo Library Compactor), but 
  162.          surely not for TOPT (Turbo OPTimizer).
  163.  
  164.  
  165.  
  166.  
  167.  
  168.                                       Page 3
  169.  
  170.  
  171.  
  172.  
  173.  
  174.              The loop structure used is not very fancy or efficient, but 
  175.          it does give a basic idea of the speed of various operations.
  176.          Obviously you could produce a much more accurate test, but this
  177.          was used for now:
  178.  
  179.              Program BenchMarkTests;
  180.  
  181.              Uses CRT;
  182.  
  183.              Type
  184.                MaxString = String;              { same as string[255] }
  185.  
  186.              Var
  187.                MasterClock         : Word ABSOLUTE $0000:$046C;
  188.                StartTime, StopTime : Word;
  189.                OuterLoop,                 
  190.                InnerLoop, A        : Word;
  191.  
  192.              Begin
  193.                For OuterLoop := 1 to 5 do
  194.                  Begin
  195.                    { any necessary assignments here }
  196.                    StartTime := MasterClock;
  197.  
  198.                    For InnerLoop := 1 to 10000 do    { 10000 varies }
  199.                      Begin
  200.                        A := 0;
  201.                        Repeat
  202.                          Inc(A);
  203.                          
  204.                          { test code goes here }
  205.  
  206.                        Until A = 255;
  207.                      End;     { of Inner Loop }
  208.  
  209.                    StopTime := MasterClock;
  210.                    Writeln(StopTime - StartTime,' Master Clock Ticks.');
  211.                  End;     { of Outer Loop }
  212.  
  213.                Sound(1000);
  214.                While not KeyPressed do;       { nothing }
  215.                NoSound;
  216.              End.
  217.              
  218.              After determining how long the empty loop took to run, that
  219.          amount was subtracted from the final clock tick count.  The 
  220.          result was that 0 master clock ticks were indicated when the loop 
  221.          was run (empty), give or take 1 clock tick.
  222.  
  223.  
  224.  
  225.  
  226.  
  227.  
  228.  
  229.  
  230.  
  231.                                       Page 4
  232.  
  233.  
  234.  
  235.  
  236.  
  237.          Value Assignment speed tests:
  238.  
  239.          One set of tests involved assigning a value to a variable.  In 
  240.          this case different types of variables were used but the value was 
  241.          always 100.  For instance:
  242.  
  243.              Num := 100;
  244.  
  245.          Also, 100 was assigned to a variable X of the same type as Num, 
  246.          and was timed as follows:
  247.              
  248.              Num := X;
  249.  
  250.          Both methods stored a value of 100 into num.  Here are the 
  251.          results:
  252.  
  253.              Variable | Clock ticks during 2,560,000 repetitions
  254.                type   |   Num := 100   |   Num := X   
  255.              _________|________________|_______________
  256.              ShortInt |      159       |      200
  257.              Byte     |      159       |      200
  258.              Integer  |      200       |      250
  259.              Word     |      200       |      250
  260.              LongInt  |      405       |      544
  261.  
  262.          So assignments work faster on byte size quantities, which was 
  263.          surprising since the 8086 family normally works with Word 
  264.          size quantities.  LongInts take about twice as long as Integers, 
  265.          as expected.  So if you are in a hurry, don't use LongInts where 
  266.          Integers or Words will suffice.
  267.  
  268.              How about value assignment between different types of 
  269.          integers?  After executing each assignment about 2.5 million 
  270.          times, the following was learned (keep in mind that these values 
  271.          are approximate and rounded off):
  272.  
  273.                Integer Type Transfer  |  Master clock ticks recorded  
  274.              _________________________|_______________________________
  275.              Shortint to Byte         |  200  (232,960   per second)
  276.              Byte to ShortInt         |  200  (232,960   per second)
  277.              Integer to Word          |  249  (187,116.5 per second)
  278.              Word to Integer          |  249  (187,116.5 per second)
  279.              ShortInt to Integer      |  249  (187,116.5 per second)
  280.              Byte to Integer          |  276  (168,811.6 per second)
  281.              ShortInt to LongInt      |  429  (108,606.1 per second)
  282.              Byte to LongInt          |  479  ( 97,269.3 per second)
  283.              Integer to LongInt       |  429  (108,606.1 per second)
  284.              Word to LongInt          |  455  (102,400   per second)
  285.              LongInt to LongInt       |  543  ( 85,804.8 per second)
  286.  
  287.  
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294.                                       Page 5
  295.  
  296.  
  297.  
  298.  
  299.  
  300.          Incrementing / Decrementing Numbers:
  301.  
  302.              As for incrementing and decrementing numbers, the following 
  303.          information was accumulated after 64 million repetitions of each 
  304.          method:
  305.  
  306.                        | Clock Ticks |
  307.                        |   during    |
  308.                Method  |  64 M Reps  |  Times per second (about)
  309.              __________|_____________|____________________________
  310.               X + 1    |    1,401    |    831,406 
  311.               Succ(X)  |    1,275    |    913,568.6
  312.               Inc(X)   |      883    |  1,319,139.3
  313.                        |             |
  314.               X - 1    |    1,404    |    829,629.6
  315.               Pred(X)  |    1,275    |    913,568.6
  316.               Dec(X)   |      883    |  1,319,139.3
  317.  
  318.               Percentage faster than "X + 1:"
  319.  
  320.                    Succ(X) =  9%
  321.                    Inc(X)  = 37%
  322.  
  323.               Percentage faster than "X - 1:"
  324.  
  325.                    Pred(X) =  9%
  326.                    Dec(X)  = 37%
  327.  
  328.  
  329.          Control Variable (Counters) speed tests:
  330.  
  331.               In using various integer types as control variables 
  332.          (counters) in loops, the following results were noted:
  333.  
  334.               After about 25 million repetitions, there was no significant 
  335.          difference in speed between Bytes and ShortInts.
  336.  
  337.               After about 170 million repetitions it was noted that Words 
  338.          (unsigned) perform about 4% faster than Integers (Signed).
  339.  
  340.               When used as counters in a for loop:
  341.  
  342.                    Byte values count the fastest (about 19% faster than 
  343.                    Integers).
  344.                    Words count about 4% faster than Integers.
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351.  
  352.  
  353.  
  354.  
  355.  
  356.  
  357.                                       Page 6
  358.  
  359.  
  360.  
  361.  
  362.  
  363.          Loop Structure speed tests:
  364.  
  365.               In comparing the various looping structures the FOR loop was 
  366.          the slowest because of the counter variable.  An interesting item 
  367.          came to light in comparing the following loops (after more than 25 
  368.          million repetitions):   { X := 0 to start }
  369.  
  370.               While X < 255 do
  371.                 Inc(X);
  372.  
  373.               Repeat 
  374.                 Inc(X);
  375.               Until X = 255;
  376.  
  377.               The REPEAT ... UNTIL loop ran about 9% faster than the WHILE 
  378.          loops.
  379.  
  380.  
  381.          Procedure and Function speed tests:
  382.  
  383.               In comparing Procedures and Functions the following were 
  384.          used:
  385.  
  386.               Procedure Test;
  387.               Begin
  388.                 A := Succ(A);
  389.               End;
  390.               (Where A is a global variable and the command timed was 
  391.                "Test;")
  392.  
  393.               Procedure Test( Var A : <type> );
  394.               Begin
  395.                 A := Succ(A);
  396.               End;
  397.               (Where A is passed as a variable (by reference) and the 
  398.                command timed was "Test(A);")
  399.  
  400.               Function Test( A : <type> ) : <type>;
  401.               Begin
  402.                 Test := Succ(A);
  403.               End;
  404.               (Where the command timed was "A := Test(A);")
  405.  
  406.          The results were much as you would expect.
  407.  
  408.          In all cases the procedure which altered a global variable 
  409.          performed the fastest.
  410.  
  411.               
  412.                             |    Clock ticks during .25 M reps
  413.                  Method     |  Byte  |  Word  | Integer | LongInt 
  414.             ________________|________|________|_________|__________
  415.             Procedure Test  |  189   |  196   |   197   |   263
  416.             Proc Test(Var A |  257   |  265   |   265   |   328
  417.             Function Test(A |  246   |  263   |   282   |   372
  418.  
  419.  
  420.                                       Page 7
  421.  
  422.  
  423.  
  424.  
  425.  
  426.              For Procedures altering global variables, Bytes are fastest, 
  427.          LongInts are slowest.
  428.  
  429.              For Procedures altering variables passed as a variable 
  430.          parameter, again Bytes are fastest, LongInts are slowest.
  431.  
  432.              For Functions Bytes are also fastest, LongInts are slowest.
  433.  
  434.              In most cases a function is faster than a procedure which uses 
  435.          a variable parameter.  The only real surprise is that when 
  436.          altering variables of type integer, a procedure using a variable 
  437.          parameter is faster than a function.
  438.  
  439.  
  440.          String Manipulation speed tests:
  441.  
  442.              Page 121 of the 4.0 manual recommends using string library 
  443.          routines such as LENGTH and COPY rather than directly accessing 
  444.          the internal string structure.  Example:
  445.  
  446.              Type 
  447.                Str : String[255];
  448.  
  449.              Var
  450.                Name       : Str;
  451.                LengthByte : Byte ABSOLUTE Name;
  452.  
  453.           In the above circumstances the following is true:
  454.  
  455.              Length(Name) = Ord(Name[0]) = LengthByte
  456.              (All are equal)
  457.  
  458.          So comparison tests were run using techniques mentioned in the 
  459.          Turbo Optimizer Manual, the November 1987 PC Magazine, and Brian 
  460.          Foley's (76317,3247) quick string manipulation demonstration 
  461.          (QKSTR.PAS).  The results were VERY interesting!
  462.  
  463.              Averages were determined after executing each item about 2.25 
  464.          million times.
  465.  
  466.  
  467.  
  468.  
  469.  
  470.  
  471.  
  472.  
  473.  
  474.  
  475.  
  476.  
  477.  
  478.  
  479.  
  480.  
  481.  
  482.  
  483.                                       Page 8
  484.  
  485.  
  486.  
  487.  
  488.  
  489.          String Length Determination:
  490.  
  491.                   TestString := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.abcdef...';
  492.  
  493.                   Str1 := Length(TestString);
  494.                   Str1 := Ord(TestString[0]);
  495.                   Str1 := LengthByte;
  496.  
  497.              After over 2 million repetitions there was NO significant 
  498.          difference in speed between any of the above techniques!  Borland 
  499.          has greatly improved the speed of Turbo's Length Function!
  500.  
  501.              Delete last character from string:
  502.  
  503.                   Delete(TestString,LengthByte,1);
  504.                   TestString[0] := Pred(TestString[0]);
  505.                   Dec(TestString[0]);
  506.                   LengthByte := Pred(LengthByte);
  507.                   Dec(LengthByte);
  508.  
  509.              DEC(TestString[0]) and DEC(LengthByte) were the fastest, 
  510.          operating at about the same speed.  This method is over 3,000% 
  511.          faster than using the DELETE procedure.
  512.  
  513.              TestString[0] := PRED(TestString[0]) and LengthByte := 
  514.          PRED(LengthByte) operated at about the same speed and were over 
  515.          2,600% faster than Turbo's DELETE procedure.
  516.  
  517.              Delete first character from string:
  518.  
  519.                   Delete(TestString,1,1);
  520.  
  521.                   Move(TestString[2],TestString[1],Pred(LengthByte);
  522.                   Dec(LengthByte);
  523.  
  524.              The latter performed about 4 1/2 times faster than the former.
  525.  
  526.  
  527.          Screen I/O speed tests:
  528.  
  529.              As for screen I/O, 4.0 incorporates tremendous improvement in 
  530.          speed over 3.x.
  531.  
  532.              The manual mentions a predefined variable "DirectVideo" which 
  533.          determines whether information is placed directly into video 
  534.          memory, or sent through the BIOS.  This variable is only effective 
  535.          when your program includes the CRT unit in a "uses" clause (i.e. 
  536.          'Uses CRT;').
  537.  
  538.              When direct video memory writing is used, write and writeln 
  539.          operate about 3 times faster than using BIOS.  Quite an 
  540.          improvement!  But FastWriteNA (one of the routines listed in 
  541.          "FASTWR.PAS - Version 2.1) performs about 67 times faster than 
  542.          Turbo even when Turbo is using direct video memory writing.
  543.  
  544.  
  545.  
  546.                                       Page 9
  547.  
  548.  
  549.  
  550.  
  551.  
  552.          Conclusion:
  553.  
  554.              These certainly aren't the most accurate or most exhaustive 
  555.          benchmark tests that could be performed, but they should give you 
  556.          an idea of how version 4.0 compares with version 3.x.  Some of the 
  557.          tricks used to increase speed in version 3.x are no longer 
  558.          necessary, while others are still useful.
  559.  
  560.              I would sure appreciate hearing about the things other people 
  561.          are finding.  If you come up with more accurate info, better speed 
  562.          up techniques, or whatever, please place your info online for the 
  563.          rest of us.  You could also drop me a note:  Bob Falk (El Paso, 
  564.          Texas), CompuServe ID# 71420,2431.
  565.  
  566.  
  567.  
  568.  
  569.  
  570.  
  571.  
  572.  
  573.  
  574.  
  575.  
  576.  
  577.  
  578.  
  579.  
  580.  
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.  
  601.  
  602.  
  603.  
  604.  
  605.  
  606.  
  607.  
  608.  
  609.                                       Page 10
  610.  
  611.  
  612.  
  613.  
  614.  
  615.              As an afterthought the following routines were included, 
  616.          they may be useful.  These routines are very easy use.  They 
  617.          resemble similar routines with the same names in Clipper, dBase, 
  618.          FoxBase, QuickSilver, etc.
  619.  
  620.              They are heavily commented (too much probably), so you may 
  621.          want to remove all or most of the comments if their size is not to 
  622.          your liking.  You can do whatever you want with them.
  623.  
  624.              They are:
  625.  
  626.                   Procedure Upper(Var Strng : MaxString);
  627.                     Converts all lower-case characters in a string to            
  628.                     uppercase.
  629.  
  630.                   Procedure Lower(Var Strng : MaxString);
  631.                     Converts all uppercase characters in a string to 
  632.                     lower-case.
  633.  
  634.                   Procedure Wait;
  635.                     Clears the keyboard buffer and waits for a keystroke 
  636.                     before returning control to the program.
  637.  
  638.              Both UPPER and LOWER are very fast!
  639.  
  640.  
  641.          Explanation:
  642.  
  643.              Page 362 of the Turbo Manual (4.0) gives an example of an 
  644.          external procedure to convert all characters in a string to 
  645.          uppercase, but I'm not too swift with assembler so the 
  646.          simple InLine approach seemed better here.
  647.  
  648.              According to chapter 26 of the Turbo Pascal 4.0 manual, 
  649.          parameters are passed to procedures and functions via the stack.  
  650.          In the case of the functions UPPER and LOWER we only pass one 
  651.          parameter and in both cases it is a variable parameter (Var Strng: 
  652.          MaxString), therefore is is passed by reference.  This means that 
  653.          what is placed on the stack is not the string itself, but a 
  654.          pointer to where the actual string is stored.  By the way, all 
  655.          string type parameters are now passed this way (as a pointer, page 
  656.          358 Turbo Manual).
  657.  
  658.              Fortunately the manual for version 4.0 provides a great 
  659.          deal of information on how all this stuff is passed.  On page 358 
  660.          we are told that a pointer type parameter is passed as a double 
  661.          word ($<segment>:$<offset>).  The segment is pushed first, and the 
  662.          offset is pushed next.
  663.  
  664.              On page 366 of the manual we learn that "the value of a 
  665.          variable identifier in an inline element is the offset address of 
  666.          the variable within its base segment."  Also we note that the 
  667.          variable offset is relative to the BP register.  Now, in Jeff 
  668.          Duntemann's superb book "Turbo Pascal Solutions," he explains not 
  669.          only how all this works, but also how to use that information in 
  670.  
  671.  
  672.                                       Page 11
  673.  
  674.  
  675.  
  676.  
  677.  
  678.          actual practice.  He even includes the "Eyeball Inline Assembler" 
  679.          to help you figure out which opcode refers to what mnemonic.  
  680.          Remarkably, even though we are using a VERY different (much 
  681.          improved) version of Turbo Pascal, Mr. Duntemann's book is by no 
  682.          means obsolete.  Such a useful book as "Turbo Pascal Solutions" 
  683.          should be on every Turbo Pascal programmer's bookshelf.  Anyway, 
  684.          appendix A of "Turbo Pascal Solutions" includes the opcodes used 
  685.          to address pointer references (var parameters).  He uses ES:[DI] 
  686.          by placing the offset to the parameter (the last value pushed onto 
  687.          the stack) into DI and the segment (the first value pushed onto 
  688.          the stack) into ES.  He even explains how to get this 32-bit 
  689.          address off the stack and into ES and DI, which is a wonderful 
  690.          thing because I know beans about Assembler.  Beginning on page 90 
  691.          of "Turbo Pascal Solutions" is an excellent explanation of how all 
  692.          this stuff works.
  693.  
  694.              The instruction LES (Load pointer using ES) takes the 32 bit 
  695.          address (the pointer to our string location) off of the stack and 
  696.          puts the segment portion into ES (it doesn't really take of OFF 
  697.          the stack, it just gets it from the stack), and the offset portion 
  698.          into the register that you specify.  For example, LES DI,STRNG[BP] 
  699.          means take the segment portion of parameter STRNG and put it in 
  700.          ES, and take the offset portion of STRNG and put it in DI 
  701.          (remember that the variable offset is relative to BP).  Now we can 
  702.          access our string and since in this procedure we are going to 
  703.          change the string - rather than return a new modified string as we 
  704.          would if this was a function - we don't have to mess with the 
  705.          methods used to return a different pointer on the stack.
  706.  
  707.              If you don't like the names of these procedures you may change 
  708.          them.  I like them because they are similar to the UPPER and LOWER 
  709.          functions in Clipper which I've used a lot.  Also, in my own use I 
  710.          don't declare STRNG as type MaxString, but rather as "String232".  
  711.          The declaration section of the program looks like this:
  712.  
  713.              Type
  714.                String232 = String[232];
  715.  
  716.          Why?  Because wide carriage printer, in compressed type face 
  717.          (17 pitch) can still only fit 232 characters on a line.  So I
  718.          rarely use strings larger than 232 characters.  So why not just 
  719.          save 23 bytes here and there?
  720.  
  721.          ------------------------------------------------------------------
  722.  
  723.  
  724.  
  725.  
  726.  
  727.  
  728.  
  729.  
  730.  
  731.  
  732.  
  733.  
  734.  
  735.                                       Page 12
  736.  
  737.  
  738.  
  739.  
  740.  
  741.          Procedure Upper( Var Strng : MaxString );
  742.          Begin
  743.            Inline
  744.              ($C4/$BE/STRNG/    {      LES DI,STRNG[BP]  ; get string     }
  745.                                 {                      address from stack }
  746.               $31/$C9/          {      XOR CX,CX       ; clear CX to zero }
  747.               $26/$8A/$0D/      {      MOV CL,ES:[DI}  ; place 1st byte of}
  748.                                 {        string (the length byte) into CL }
  749.               $E3/$13/          {      JCXZ +13h (19 bytes) ; if CX is 0  }
  750.                                 {        (null string) then jump +19 bytes}
  751.               $47/              {CNV:  INC DI ; increment pointer to      }
  752.                                 {        point to next character in STRNG }
  753.               $26/$80/$3D/$7A/  {      CMP BYTE PTR ES:[DI],122 ; compare }
  754.                                 {        character with a lower-case 'z'  }
  755.               $77/$0A/          {      JA +Ah (10 bytes) ; if char is >'z'}
  756.                                 {        skip it and move on (it's extnded}
  757.               $26/$80/$3D/$61/  {      CMP BYTE PTR ES:[DI],97 ; compare  }
  758.                                 {        character with lower-case 'a'    }
  759.               $72/$04/          {      JB +4 (4 bytes) ; if char is < 'a' }
  760.                                 {        it's not lower-case so move on   }
  761.               $26/$80/$2D/$20   {      SUB BYTE PTR ES:[DI],32 ; subtract }
  762.                                 {        32 (hex 20) from char (convert)  }
  763.               $E2/$ED);         {MORE: LOOP -13h (19 bytes) ; Loop back to}
  764.                                 {        CNV and check next character     }
  765.                                 {DONE:   ; That's it, we are finished     }
  766.          End;                   {          end of procedure UPPER         }
  767.  
  768.  
  769.          Procedure Lower( Var Strng : MaxString );
  770.          Begin
  771.            Inline
  772.              ($C4/$BE/STRNG/    {      LES DI,STRNG[BP]  ; get string     }
  773.                                 {                      address from stack }
  774.               $31/$C9/          {      XOR CX,CX       ; clear CX to zero }
  775.               $26/$8A/$0D/      {      MOV CL,ES:[DI}  ; place 1st byte of}
  776.                                 {        string (the length byte) into CL }
  777.               $E3/$13/          {      JCXZ +13h (19 bytes) ; if CX is 0  }
  778.                                 {        (null string) then jump +19 bytes}
  779.               $47/              {CNV:  INC DI ; increment pointer to      }
  780.                                 {        point to next character in STRNG }
  781.               $26/$80/$3D/$5A/  {      CMP BYTE PTR ES:[DI],90 ; compare  }
  782.                                 {        character with an uppercase 'Z'  }
  783.               $77/$0A/          {      JA +Ah (10 bytes) ; if char is >'Z'}
  784.                                 {        it's not uppercase (move on)     }
  785.               $26/$80/$3D/$41/  {      CMP BYTE PTR ES:[DI],65 ; compare  }
  786.                                 {        character with an uppercase 'A'  }
  787.               $72/$04/          {      JB +4 (4 bytes) ; if char is < 'A' }
  788.                                 {        it's a control char so move on   }
  789.               $26/$80/$05/$20   {      ADD BYTE PTR ES:[DI],32 ; add 32   }
  790.                                 {        (hex 20) to character (convert)  }
  791.               $E2/$ED);         {MORE: LOOP -13h (19 bytes) ; Loop back to}
  792.                                 {            CNV and check next character }
  793.                                 {DONE:   ; That's it, we are finished     }
  794.          End;                   {          end of procedure UPPER         }
  795.          (Note: $ED is two's compliment for -19.)
  796.  
  797.  
  798.                                       Page 13
  799.  
  800.  
  801.  
  802.  
  803.  
  804.          Procedure Wait;
  805.          { Wait uses DOS Interrupt 12 ($C).  It's purpose is to wait for a 
  806.            keystroke before returning control to the program, it doesn't
  807.            return anything.  It clears the keyboard buffer (in RAM) to 
  808.            prevent accidental continuation due to characters stored in the
  809.            keyboard buffer.  It was used because of the quirks in the Turbo 
  810.            Pascal KeyPressed function.  I still use it because it is like 
  811.            the WAIT procedure in Clipper and dBase, and it's easier to use
  812.            than "While not KeyPressed do;".
  813.  
  814.            DOS Interrupt $C is the "Clear Keyboard and Do" function.  It 
  815.            clears the keyboard buffer and then executes one of 5 available
  816.            DOS services (1,6,7,8 or A).  The service is determined by 
  817.            placing it's number into the AL register (remember AH contains
  818.            $C).  In this case we will choose service 7 - Direct Keyboard
  819.            Input (without echo).  The result will be that the keyboard 
  820.            buffer will be cleared, and the procedure will wait for a 
  821.            keystroke before returning control to the main program.  
  822.          }
  823.          Begin
  824.            Inline
  825.              ($B4/$0C/      { MOV AH,0Ch  ; Place $C in AH. DOS Clear Key-}
  826.                             {               board and Do service          }
  827.               $B0/$07/      { MOV AL,7    ; Place 7 in AL.  DOS Direct    }
  828.                             {               Console Input (without echo)  }
  829.               $CD/$21);     { INT 21h     ; Call Interrupt 33, DOS Func-  }
  830.                             {               tion request                  }
  831.          End;               {           end of Procedure Wait             }
  832.  
  833.  
  834.          NOTE:  I didn't want to upload 2 files in the .ARC file, so you'll 
  835.          have to remove these procedures from this file with your word 
  836.          processor, and then probably clean up the comments.  They're there 
  837.          so someone not too familiar with Assembler could understand them.  
  838.  
  839.  
  840.  
  841.  
  842.  
  843.  
  844.  
  845.  
  846.  
  847.  
  848.  
  849.  
  850.  
  851.  
  852.  
  853.  
  854.  
  855.  
  856.  
  857.  
  858.  
  859.  
  860.  
  861.                                       Page 14
  862.